package com.ihr360.payroll.service.staff.impl;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.ihr360.authority.emmbed.base.annotation.CheckStaffIdList;
import com.ihr360.common.DateRange;
import com.ihr360.commons.context.session.Ihr360SessionContextHolder;
import com.ihr360.commons.entity.AuthUserInfo;
import com.ihr360.commons.exception.Ihr360Exception;
import com.ihr360.commons.exception.Ihr360RuntimeException;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.lang.DateUtils;
import com.ihr360.commons.lang.StringUtils;
import com.ihr360.commons.model.meta.table.DynamicCell;
import com.ihr360.commons.model.type.ICompanyId;
import com.ihr360.commons.utils.JpaCriteriaPathUtils;
import com.ihr360.commons.vo.PageData;
import com.ihr360.commons.vo.ResultInfo;
import com.ihr360.entity.BasicStaffInfoEntity;
import com.ihr360.entity.StaffBankSalaryEntity;
import com.ihr360.excel.ExcelUtil;
import com.ihr360.excel.cellstyle.ExcelCellStyle;
import com.ihr360.excel.cellstyle.ExcelCellStyleFactory;
import com.ihr360.excel.entity.CorrectSpreadsheetRowEntity;
import com.ihr360.excel.entity.ExportParams;
import com.ihr360.excel.vo.ImportRequest;
import com.ihr360.hibernate.type.util.ObjectMapperWrapper;
import com.ihr360.payroll.calc.StaffSalaryCalcPeriod;
import com.ihr360.payroll.dao.CompanySalaryProfileRepository;
import com.ihr360.payroll.dao.StaffPayrollInfoJournalRepository;
import com.ihr360.payroll.dao.StaffPayrollInfoRepository;
import com.ihr360.payroll.dao.StaffSalaryProfileRepository;
import com.ihr360.payroll.dao.SystemSalaryFieldRepository;
import com.ihr360.payroll.dao.TaxDivisionRepository;
import com.ihr360.payroll.entity.staff.CompanySalaryProfileEntity;
import com.ihr360.payroll.entity.staff.SalaryProfileEntity;
import com.ihr360.payroll.entity.staff.SalaryProfileFieldEntity;
import com.ihr360.payroll.entity.staff.StaffSalaryEntity;
import com.ihr360.payroll.entity.staff.StaffSalaryProfileEntity;
import com.ihr360.payroll.entity.staff.StaffSalaryProfileMapperEntity;
import com.ihr360.payroll.model.dto.salary.basecode.TaxDivision;
import com.ihr360.payroll.model.dto.salary.meta.system.SystemSalaryField;
import com.ihr360.payroll.model.dto.salary.profile.CompanySalaryProfile;
import com.ihr360.payroll.model.dto.staff.SalaryProfileField;
import com.ihr360.payroll.model.dto.staff.StaffPayrollInfo;
import com.ihr360.payroll.model.dto.staff.StaffPayrollInfoJournal;
import com.ihr360.payroll.model.dto.staff.StaffSalaryProfile;
import com.ihr360.payroll.model.dto.staff.embeddable.SalaryProfile;
import com.ihr360.payroll.model.dto.staff.embeddable.SalaryProfileItemsWrapper;
import com.ihr360.payroll.model.dto.staff.embeddable.StaffBankCard;
import com.ihr360.payroll.request.SalaryProfileFieldRequest;
import com.ihr360.payroll.request.SalaryProfileTypeItem;
import com.ihr360.payroll.request.StaffPayrollInfoRequest;
import com.ihr360.payroll.request.StaffPayrollInfosRequest;
import com.ihr360.payroll.request.StaffSalaryProfileSearchBody;
import com.ihr360.payroll.response.StaffPayrollInfoResponse;
import com.ihr360.payroll.service.salaryplan.SalaryPlanStaffService;
import com.ihr360.payroll.service.salaryprofile.CompanySalaryProfileService;
import com.ihr360.payroll.service.salaryprofile.convertor.CompanySalaryProfileConvertor;
import com.ihr360.payroll.service.staff.StaffPayrollInfoJournalService;
import com.ihr360.payroll.service.staff.StaffPayrollInfoService;
import com.ihr360.payroll.service.staff.convertor.StaffPayRollInfoList2StaffSalaryCalcPeriodConvertor;
import com.ihr360.payroll.service.staff.convertor.StaffSalaryProfileConvertor;
import com.ihr360.payroll.service.staff.validator.AbnormalityStaffSalaryProfileValidator;
import com.ihr360.payroll.service.staff.validator.StaffPayrollInfosValidator;
import com.ihr360.payroll.type.SalaryProfileChangeType;
import com.ihr360.rest.reposity.SimpleRestReposity;
import com.ihr360.rest.service.AbstractSimpleRestServiceImpl;
import com.ihr360.shared.basicstaffinfo.convertor.SyncedBasicStaffInfoConvertor;
import com.ihr360.shared.basicstaffinfo.dao.SyncedStaffInfoRepository;
import com.ihr360.shared.basicstaffinfo.dto.SyncedBasicStaffInfo;
import com.ihr360.type.StaffStatus;
import com.ihr360.validation.ValidationUtils;
import com.ihr360.validation.annonation.EnableValidation;
import com.ihr360.validation.annonation.Valid;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections.map.HashedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.ServletOutputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;
import java.util.function.Function;
import java.util.stream.Collectors;


@Service
public class StaffPayrollInfoServiceImpl extends AbstractSimpleRestServiceImpl<StaffSalaryProfile, String> implements StaffPayrollInfoService {

    Logger logger = LoggerFactory.getLogger(StaffPayrollInfoServiceImpl.class);

    public static final String STAFF_SALARY_IMPORT_HEADER_KEY_NAME = "name";
    public static final String STAFF_SALARY_IMPORT_HEADER_KEY_MOBILE = "mobileNo";
    public static final String STAFF_SALARY_IMPORT_HEADER_KEY_EFFECTIVEAT = "effectiveAt";
    public static final String STAFF_SALARY_IMPORT_HEADER_KEY_ADJUST_RESSON = "adjustReason";

    public static final Map<String, String> importConstant = new HashMap<String, String>() {
        {
            put(STAFF_SALARY_IMPORT_HEADER_KEY_NAME, "姓名");
            put(STAFF_SALARY_IMPORT_HEADER_KEY_MOBILE, "手机号");
            put(STAFF_SALARY_IMPORT_HEADER_KEY_EFFECTIVEAT, "生效日期");
            put(STAFF_SALARY_IMPORT_HEADER_KEY_ADJUST_RESSON, "调整原因");
        }
    };


    @Autowired
    private StaffSalaryProfileRepository staffSalaryProfileRepository;

    @Autowired
    private SyncedStaffInfoRepository basicStaffInfoReposity;

    @Autowired
    private StaffSalaryProfileConvertor staffSalaryProfileConvertor;

    @Autowired
    private CompanySalaryProfileRepository companySalaryProfileRepository;

    @Autowired
    private CompanySalaryProfileService companySalaryProfileService;

    @Autowired
    private EntityManager entityManager;

    @Autowired
    private StaffPayrollInfoJournalRepository staffPayrollInfoJournalRepository;

    @Autowired
    StaffPayRollInfoList2StaffSalaryCalcPeriodConvertor payRollInfoList2StaffSalaryCalcPeriodConvertor;

    @Autowired
    private StaffPayrollInfoJournalService staffPayrollInfoJournalService;

    @Autowired
    private SyncedBasicStaffInfoConvertor syncedBasicStaffInfoConvertor;

    @Autowired
    private CompanySalaryProfileConvertor companySalaryProfileConvertor;

    @Autowired
    private SyncedStaffInfoRepository syncedStaffInfoRepository;

    @Autowired
    private SystemSalaryFieldRepository systemSalaryFieldRepository;

    @Autowired
    private StaffPayrollInfoRepository staffPayrollInfoRepository;

    @Autowired
    private SalaryPlanStaffService salaryPlanStaffService;

    @Autowired
    private TaxDivisionRepository taxDivisionRepository;


    @Override
    public SimpleRestReposity<StaffSalaryProfile, String> getSimpleRestReposity() {
        return staffSalaryProfileRepository;
    }


    /**
     * 查询出 周期范围内的分段薪资数据
     * <p>
     * case 1:
     * ------------------->startOn+---------------------+endOn
     * ------->effectiveAt +------------+InValidAt
     * -------------->effectiveAt +---------------------+InValidAt
     * ------->effectiveAt +---------------------------------+InValidAt
     * ------->effectiveAt +-----------------------------------------------+max
     * <p>
     * case 2:
     * ------------------->startOn+---------------------+endOn
     * --------------------->effectiveAt +------------+InValidAt
     * --------------------->effectiveAt +-------------------+InValidAt
     * <p>
     *
     * @param staffIds
     * @param dateRange
     * @return
     */
    @Override
    public Map<String, StaffSalaryCalcPeriod> getPeriodSalaryProfileOfStaffs(String companyId, Collection<String> staffIds, DateRange dateRange) {
        //如果没有薪资数据

        ValidationUtils.rejectIfEmpty(companyId, null, "无法获取公司标识");
        ValidationUtils.rejectIfEmpty(staffIds, null, "员工id列表不能为空");

        Specification<StaffSalaryProfile> specification = getPeriodSpecification(companyId, dateRange, staffIds);
        List<StaffSalaryProfile> staffInfos = staffSalaryProfileRepository.findAll(specification);
        Map<String, List<StaffSalaryProfile>> groups = staffInfos.stream().filter(s -> s.getStaff() != null)
                //.sorted((s1, s2) -> s1.getSalaryProfile().getEffectiveAt().compareTo(s2.getSalaryProfile().getEffectiveAt()))
                .collect(Collectors.groupingBy(si -> si.getStaff().getId()));

        return payRollInfoList2StaffSalaryCalcPeriodConvertor.convertTo(groups);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    @EnableValidation
    public Boolean adjustSalaryProfileWrapper(@Valid(validator = StaffPayrollInfosValidator.class) StaffPayrollInfosRequest param) {
        List<String> staffIds = Lists.newArrayList();
        List<StaffPayrollInfoRequest> staffPayrollInfoRequests = param.getPayrollInfos();
        if (CollectionUtils.isNotEmpty(staffPayrollInfoRequests)) {
            staffIds = staffPayrollInfoRequests.stream().map(StaffPayrollInfoRequest::getStaffId).collect(Collectors.toList());
        }

        return ((StaffPayrollInfoService) AopContext.currentProxy()).adjustSalaryProfileWrapperNoValid(staffIds, param);
    }


    @Override
    @Async
    @Transactional(rollbackFor = Exception.class)
    @CheckStaffIdList
    public Boolean adjustSalaryProfileWrapperNoValid(List<String> staffIds, StaffPayrollInfosRequest param) {

        String companyId = Ihr360SessionContextHolder.getCompanyId();
        if (StringUtils.isEmpty(companyId)) {
            companyId = param.getPayrollInfos().get(0).getCompanyId();
        }
        List<StaffPayrollInfoRequest> requestPayrollInfos = param.getPayrollInfos();

        Set<String> requestStaffIds = requestPayrollInfos.stream().map(StaffPayrollInfoRequest::getStaffId).collect(Collectors.toSet());

        List<SystemSalaryField> systemSalaryFields = (List<SystemSalaryField>) systemSalaryFieldRepository.findAll();
        Map<String, String> systemStringStringMap = systemSalaryFields.stream().collect(Collectors.toMap(SystemSalaryField::getCode, SystemSalaryField::getName));

        setSalaryfieldName(requestPayrollInfos, systemStringStringMap);

        List<StaffSalaryProfile> existStaffPayrollInfos = staffSalaryProfileRepository.findByCompanyIdAndStaffIdIn(companyId, requestStaffIds);
        Set<String> existStaffIds = Sets.newHashSet();
        if (CollectionUtils.isNotEmpty(existStaffPayrollInfos)) {
            Map<String, List<StaffSalaryProfile>> existStaffPayrollInfoMap = existStaffPayrollInfos.stream()
                    .collect(Collectors.groupingBy(s -> s.getStaff().getId()));
            checkStaffpayrollInfoValid(existStaffPayrollInfoMap);
            existStaffIds = existStaffPayrollInfoMap.keySet();
        } else {
            //不允许添加今天之后的生效数据
            boolean hasEffectiveData = requestPayrollInfos.stream()
                    .map(StaffPayrollInfoRequest::getTypeItems)
                    .flatMap(item -> item.stream().filter(i -> i.getEffectiveAt() != null
                            && DateUtils.getMaxDateInDay(i.getEffectiveAt()).getTime() <= DateUtils.getMaxDateInDay(new Date()).getTime()))
                    .count() >= 1;
            if (!hasEffectiveData) {
                throw new Ihr360Exception(null, "不存在当前生效的薪资记录！");
            }
        }
        requestStaffIds.removeAll(existStaffIds);
        if (CollectionUtils.isNotEmpty(requestStaffIds)) {
            List<SyncedBasicStaffInfo> noSalaryProfileStaffInfos = syncedStaffInfoRepository.findAll(requestStaffIds);
            ValidationUtils.rejectIfEmpty(noSalaryProfileStaffInfos, null, "员工不存在！");
            CompanySalaryProfile companySalaryProfile = companySalaryProfileService.getOrCreateByCompanyId(companyId);
            String finalCompanyId = companyId;
            noSalaryProfileStaffInfos.forEach(syncedBasicStaffInfo -> {
                StaffSalaryProfile staffSalaryProfile = new StaffSalaryProfile();
                staffSalaryProfile.setStaff(syncedBasicStaffInfo);
                staffSalaryProfile.setCompanyId(finalCompanyId);
                SalaryProfile salaryProfile = new SalaryProfile();
                salaryProfile.setCompanySalaryProfile(companySalaryProfile);
                staffSalaryProfile.setSalaryProfile(salaryProfile);
                existStaffPayrollInfos.add(staffSalaryProfile);
            });
        }

        Map<String, StaffSalaryProfile> staffSalaryProfileMap = Maps.newHashMap();
        existStaffPayrollInfos.forEach(staffSalaryProfile -> {
            staffSalaryProfileMap.put(staffSalaryProfile.getStaff().getId(), staffSalaryProfile);
        });

        //校验数据员工是否存在薪资档案数据
//        checkExistStaffPayrollInfo(requestPayrollInfos, requestStaffIds, existStaffPayrollInfos);
        Map<String, StaffPayrollInfoRequest> requestPayrollInfoMap = requestPayrollInfos.stream()
                .collect(Collectors.toMap(StaffPayrollInfoRequest::getStaffId, Function.identity()));

        //处理数据
        List<StaffSalaryProfile> toSave = Lists.newArrayList();
        requestPayrollInfoMap.forEach((staffId, staffPayrollInfoRequest) -> {
            Set<Date> effectiveDates = Sets.newHashSet();
            List<SalaryProfileTypeItem> requestSalaryProfileItems = staffPayrollInfoRequest.getTypeItems();
            StaffSalaryProfile staffSalaryProfile = staffSalaryProfileMap.get(staffId);
            handlePayrollInfoData(staffSalaryProfile, toSave, effectiveDates, requestSalaryProfileItems);
        });
        Set<String> modifyStaffIds = requestPayrollInfoMap.keySet();
        if (CollectionUtils.isNotEmpty(modifyStaffIds)) {
            staffSalaryProfileRepository.deleteByCompanyIdAndStaffIds(companyId, modifyStaffIds);
        }
        entityManager.flush();
        staffSalaryProfileRepository.save(toSave);

        //日志
        List<StaffPayrollInfoJournal> journals = generateStaffPayrollInfoJournals(companyId, requestPayrollInfos, true);
        staffPayrollInfoJournalRepository.save(journals);
        return Boolean.TRUE;
    }


    @Override
    @Transactional
    public ResultInfo abnormalityAdjustProfileWrapper(@Valid(validator = AbnormalityStaffSalaryProfileValidator.class) StaffPayrollInfoRequest param) {
        String companyId = param.getCompanyId();
        String staffName = param.getStaffName();
        String staffId = param.getStaffId();
        List<SalaryProfileTypeItem> salaryProfileTypeItems = param.getTypeItems();

        List<StaffSalaryProfile> existStaffPayrollInfos = staffSalaryProfileRepository.findByCompanyIdAndStaffId(companyId, staffId);
        boolean existDataFlag = true;
        if (CollectionUtils.isEmpty(existStaffPayrollInfos)) {
            existDataFlag = false;
            StaffSalaryProfile staffSalaryProfile = new StaffSalaryProfile();
            SyncedBasicStaffInfo staffInfo = basicStaffInfoReposity.findOne(staffId);
            if (staffInfo == null) {
                return new ResultInfo.Builder().error("员工" + staffId + "未同步！");
            }
            staffSalaryProfile.setStaff(staffInfo);
            CompanySalaryProfile companySalaryProfile = companySalaryProfileService.getOrCreateByCompanyId(companyId);
            SalaryProfile salaryProfile = new SalaryProfile();
            salaryProfile.setCompanySalaryProfile(companySalaryProfile);
            staffSalaryProfile.setSalaryProfile(salaryProfile);
            staffSalaryProfile.setCompanyId(companyId);
            existStaffPayrollInfos.add(staffSalaryProfile);
        }

        if (existDataFlag) {
            Map<String, List<SalaryProfile>> effectiveExistProfileMap = existStaffPayrollInfos.stream()
                    .map(StaffSalaryProfile::getSalaryProfile)
                    .filter(s -> s.getEffectiveAt() != null)
                    .collect(Collectors.groupingBy(s -> DateUtils.formatDateStr(s.getEffectiveAt(), DateUtils.STANDARD_DATE_FORMAT_YM1)));
            salaryProfileTypeItems.forEach(salaryProfileTypeItem -> {
                String adjustMonth = DateUtils.formatDateStr(salaryProfileTypeItem.getEffectiveAt(), DateUtils.STANDARD_DATE_FORMAT_YM1);
                List<SalaryProfile> existProfiles = effectiveExistProfileMap.get(adjustMonth);
                if (CollectionUtils.isNotEmpty(existProfiles)) {
                    List<SalaryProfileChangeType> changeTypes = existProfiles.stream()
                            .filter(p -> p.getChangeType() == salaryProfileTypeItem.getChangeType())
                            .map(SalaryProfile::getChangeType)
                            .collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(changeTypes)) {
                        throw new Ihr360Exception(null, String.join("", staffName, adjustMonth, "已存在", changeTypes.get(0).getTitle(), "类型的记录！"));
                    }
                }
            });
            Map<String, List<StaffSalaryProfile>> existStaffPayrollInfoMap = existStaffPayrollInfos.stream()
                    .collect(Collectors.groupingBy(s -> s.getStaff().getId()));


            checkStaffpayrollInfoValid(existStaffPayrollInfoMap);
            existStaffPayrollInfos.forEach(existStaffPayrollInfo -> {
                SalaryProfile salaryProfile = existStaffPayrollInfo.getSalaryProfile();
                SalaryProfileTypeItem salaryProfileTypeItem = convertSalaryProfileTosalaryProfileTypeItem(salaryProfile);
                salaryProfileTypeItems.add(salaryProfileTypeItem);
            });
        }

        List<StaffSalaryProfile> toSave = Lists.newArrayList();
        Set<Date> effectiveDates = Sets.newHashSet();
        handlePayrollInfoData(existStaffPayrollInfos.get(0), toSave, effectiveDates, salaryProfileTypeItems);

        if (existDataFlag) {
            staffSalaryProfileRepository.delete(existStaffPayrollInfos);
        }
        staffSalaryProfileRepository.save(toSave);

        //日志
        List<StaffPayrollInfoJournal> journals = generateStaffPayrollInfoJournals(companyId, Lists.newArrayList(param), false);

        for (StaffPayrollInfoJournal journal : journals) {
            journal.setReason("i人事数据迁移");
        }
        staffPayrollInfoJournalRepository.save(journals);
        return new ResultInfo.Builder().success();
    }

    @Override
    @CheckStaffIdList
    public StaffPayrollInfoResponse getCurrentEffectiveData(List<String> checkStaffIds, StaffSalaryProfileSearchBody params) {

        StaffPayrollInfoResponse response = new StaffPayrollInfoResponse();

        Date currentDate = DateUtils.getMinDateInDay(new Date());
        List<Object[]> datas = staffSalaryProfileRepository.getCurrentEffectiveData(currentDate, params.getAuthStaffIds());
        if (CollectionUtils.isEmpty(datas)) {
            response.setPageDate(new PageData<>(0, datas.size(), Lists.newArrayList()));
            return response;
        }
        List<StaffSalaryProfileMapperEntity> staffSalaryMapper = getStaffSalaryMappers(datas);

        List<StaffSalaryProfileEntity> staffSalaryProfileEntities = Lists.newArrayList();
        List<String> exitStaffSalaryProfileIds = staffSalaryMapper.stream()
                .filter(s -> s.getStaffSalaryProfileId() != null)
                .map(StaffSalaryProfileMapperEntity::getStaffSalaryProfileId)
                .collect(Collectors.toList());
        Map<String, StaffSalaryProfile> existStaffSalaryMap = Maps.newHashMap();
        if (CollectionUtils.isNotEmpty(exitStaffSalaryProfileIds)) {
            List<StaffSalaryProfile> existStaffSalaryProfiles
                    = (List<StaffSalaryProfile>) staffSalaryProfileRepository.findAll(exitStaffSalaryProfileIds);
            existStaffSalaryMap = existStaffSalaryProfiles.stream()
                    .collect(Collectors.toMap(s -> s.getStaff().getId(), Function.identity()));
        }

        for (StaffSalaryProfileMapperEntity mapper : staffSalaryMapper) {
            if (mapper.getStaffSalaryProfileId() == null) {
                StaffSalaryProfileEntity staffSalaryProfileEntity = new StaffSalaryProfileEntity();
                BasicStaffInfoEntity basicStaffInfoEntity = new BasicStaffInfoEntity();
                basicStaffInfoEntity.setId(mapper.getStaffId());
                basicStaffInfoEntity.setStaffName(mapper.getStaffName());
                basicStaffInfoEntity.setDepartmentName(mapper.getDepartmentName());
                basicStaffInfoEntity.setStaffStatus(mapper.getStaffStatus());
                basicStaffInfoEntity.setMobileNo(mapper.getMobileNo());
                staffSalaryProfileEntity.setStaff(basicStaffInfoEntity);
                staffSalaryProfileEntities.add(staffSalaryProfileEntity);
            } else {
                StaffSalaryProfile staffSalaryProfile = existStaffSalaryMap.get(mapper.getStaffId());
                staffSalaryProfileEntities.add(staffSalaryProfileConvertor.convertDtoToEntity(staffSalaryProfile));
            }
        }

        if (CollectionUtils.isNotEmpty(staffSalaryProfileEntities)) {
            List<String> staffIds = staffSalaryProfileEntities.stream().map(s -> s.getStaff().getId()).collect(Collectors.toList());
            Map<String, List<String>> staffSalaryPlanMap = salaryPlanStaffService.getStaffNameAndStaffIdMapByStaffIdList(staffIds);

            if (MapUtils.isNotEmpty(staffSalaryPlanMap)) {
                staffSalaryProfileEntities.forEach(s -> {
                    List<String> planNames = staffSalaryPlanMap.get(s.getStaff().getId());
                    if (CollectionUtils.isNotEmpty(planNames)) {
                        String planStr = String.join(",", planNames);
                        s.setSalaryPlans(planStr);
                    }
                });
            }
        }

        response.setPageDate(new PageData<>(params.getTotalPages(), params.getTotalElements(), staffSalaryProfileEntities));

        if (params.isShowCornerInfo()) {
            Set<String> staffIds = staffSalaryProfileEntities.stream().map(s -> s.getStaff().getId()).collect(Collectors.toSet());
            List<StaffPayrollInfoJournal> effectiveAfterTodayJournals = staffPayrollInfoJournalService.findEffectiveAfterTodayJournals(staffIds);

            if (CollectionUtils.isEmpty(effectiveAfterTodayJournals)) {
                return response;
            }
            Map<String, Set<String>> staffCodeMap = Maps.newHashMap();
            effectiveAfterTodayJournals.forEach(journal -> {
                Set<String> codes = staffCodeMap.get(journal.getStaffId());
                if (CollectionUtils.isEmpty(codes)) {
                    codes = Sets.newHashSet();
                    staffCodeMap.put(journal.getStaffId(), codes);
                }
                codes.add(journal.getFieldCode());
            });
            response.setStaffCodes(staffCodeMap);
        }
        return response;
    }

    @Override
    @CheckStaffIdList
    public List<StaffSalaryProfileEntity> getAfterCurrentDatas(List<String> staffIds) {

        ValidationUtils.rejectIfEmpty(staffIds, null, "员工Id不能为空！");
        Date currentDay = DateUtils.getMaxDateInDay(new Date());
        String companyId = Ihr360SessionContextHolder.getCompanyId();
        List<StaffSalaryProfile> staffPayrollInfos = staffSalaryProfileRepository.findAfterCurrentDatas(companyId, staffIds.get(0), currentDay);
        return staffSalaryProfileConvertor.convertDtosToEntitys(staffPayrollInfos);
    }

    @Override
    @CheckStaffIdList
    public List<StaffSalaryProfileEntity> getStaffPayrollInfos(List<String> checkStaffIds) {
        String companyId = Ihr360SessionContextHolder.getCompanyId();
        ValidationUtils.rejectIfEmpty(checkStaffIds, null, "员工Id不能为空！");
        String staffId = checkStaffIds.get(0);
        Set<String> staffIds = Sets.newHashSetWithExpectedSize(1);
        staffIds.add(staffId);
        List<StaffSalaryProfile> staffPayrollInfos = staffSalaryProfileRepository.findByCompanyIdAndStaffIdIn(companyId, staffIds);
        CompanySalaryProfile companySalaryProfile = companySalaryProfileService.getOrCreateByCompanyId();
        setSalaryfieldName(companySalaryProfile);

        if (CollectionUtils.isNotEmpty(staffPayrollInfos)) {
            staffPayrollInfos.forEach(staffSalaryProfile -> {
                staffSalaryProfile.getSalaryProfile().setCompanySalaryProfile(companySalaryProfile);
            });
            return staffSalaryProfileConvertor.convertDtosToEntitys(staffPayrollInfos);
        }
        SyncedBasicStaffInfo staffInfo = basicStaffInfoReposity.getOne(staffId);
        ValidationUtils.rejectIfEmpty(staffInfo, null, "员工不存在！");


        StaffSalaryProfileEntity staffSalaryProfileEntity = new StaffSalaryProfileEntity();
        staffSalaryProfileEntity.setCompanyId(companyId);
        staffSalaryProfileEntity.setStaff(syncedBasicStaffInfoConvertor.convertDtoToEntity(staffInfo));
        SalaryProfileEntity salaryProfileEntity = new SalaryProfileEntity();
        salaryProfileEntity.setCompanySalaryProfile(companySalaryProfileConvertor.convertDtoToEntity(companySalaryProfile));
        staffSalaryProfileEntity.setSalaryProfile(salaryProfileEntity);

        return Lists.newArrayList(staffSalaryProfileEntity);
    }

    @Override
    public List<StaffSalaryProfileEntity> getStaffPayrollInfos(List<String> checkStaffIds, String companyId) {
        ValidationUtils.rejectIfEmpty(checkStaffIds, null, "员工Id不能为空！");
        Set<String> staffIds = Sets.newHashSetWithExpectedSize(1);
        String staffId = checkStaffIds.get(0);
        staffIds.add(staffId);
        List<StaffSalaryProfile> staffPayrollInfos = staffSalaryProfileRepository.findByCompanyIdAndStaffIdIn(companyId, staffIds);
        CompanySalaryProfile companySalaryProfile = companySalaryProfileService.getOrCreateByCompanyId();
        setSalaryfieldName(companySalaryProfile);

        if (CollectionUtils.isEmpty(staffPayrollInfos)) {
            return Lists.newArrayList();
        }
        staffPayrollInfos.forEach(staffSalaryProfile -> {
            staffSalaryProfile.getSalaryProfile().setCompanySalaryProfile(companySalaryProfile);
        });
        return staffSalaryProfileConvertor.convertDtosToEntitys(staffPayrollInfos);

    }

    @Override
    @CheckStaffIdList
    public List<StaffSalaryProfileEntity> getSomeStaffsPayrollInfos(List<String> staffIds) {
        List<StaffSalaryProfile> staffPayrollInfos = staffSalaryProfileRepository.findByCompanyIdAndStaffIdIn(Ihr360SessionContextHolder.getCompanyId(), Sets.newHashSet(staffIds));
        CompanySalaryProfile companySalaryProfile = companySalaryProfileService.getOrCreateByCompanyId();
        setSalaryfieldName(companySalaryProfile);

        if (CollectionUtils.isEmpty(staffPayrollInfos)) {
            return Lists.newArrayList();
        }
        staffPayrollInfos.forEach(staffSalaryProfile -> {
            staffSalaryProfile.getSalaryProfile().setCompanySalaryProfile(companySalaryProfile);
        });
        return staffSalaryProfileConvertor.convertDtosToEntitys(staffPayrollInfos);

    }


    @Override
    public List<String> probationReviewStaffIdsFromIrs(String companyId) {
        List<String> staffIds = staffPayrollInfoJournalRepository.getAllProbationReviewStaffIdsFromIrs(SalaryProfileChangeType.PROBATION_REVIEW, companyId);
        if (CollectionUtils.isEmpty(staffIds)) {
            return null;
        }
        return staffIds;
    }

    @Override
    public List<String> getStaffIdsForExistsSalaryProfile(String companyId) {
        return staffSalaryProfileRepository.findExistStaffIdsByCompanyId(companyId);
    }

    @Transactional
    @Async
    @Override
    @CheckStaffIdList
    public void save(List<String> staffIds, List<StaffSalaryProfileEntity> toSave) {
        List<StaffSalaryProfile> staffSalaryProfiles = staffSalaryProfileConvertor.convertEntitiesToDtos(toSave);
        staffSalaryProfileRepository.save(staffSalaryProfiles);
    }

    @Override
    @CheckStaffIdList
    public StaffSalaryEntity getStaffSalaryInfo(List<String> staffIds, String companyId) {
        String staffId = staffIds.get(0);
        StaffSalaryEntity staffSalaryEntity = new StaffSalaryEntity();
        staffSalaryEntity.setCompanyId(companyId);
        staffSalaryEntity.setStaffId(staffId);
        //银行卡信息
        StaffPayrollInfo staffPayrollInfo = staffPayrollInfoRepository.findByCompanyIdAndStaffId(companyId, staffId);
        if (staffPayrollInfo != null) {
            staffSalaryEntity.setWorkHourType(staffPayrollInfo.getWorkHourType());
            staffSalaryEntity.setTaxRole(staffPayrollInfo.getTaxRole());
            staffSalaryEntity.setTaxExemption(staffPayrollInfo.getTaxExemption());
            if (staffPayrollInfo.getStaffBankCard() != null) {
                StaffBankCard staffBankCard = staffPayrollInfo.getStaffBankCard();
                BeanUtils.copyProperties(staffBankCard, staffSalaryEntity);
            }
            TaxDivision taxDivision = staffPayrollInfo.getTaxDivision();
            if (taxDivision != null) {
                staffSalaryEntity.setTaxDivisionCode(taxDivision.getCode());
            }
        }
        //公司薪资项目配置
        staffSalaryEntity.setCompanySalaryProfileEntity(companySalaryProfileService.getByCompanyId(companyId));
        //员工当前薪资信息
        Date currentDate = DateUtils.getMinDateInDay(new Date());
        StaffSalaryProfile staffSalaryProfile = staffSalaryProfileRepository.getStaffCurrentSalaryProfile(companyId, staffId, currentDate);
        if (staffSalaryProfile != null) {
            staffSalaryEntity.setStaffSalary(staffSalaryProfileConvertor.convertDtoToEntity(staffSalaryProfile));
        }
        return staffSalaryEntity;
    }

    @Override
    @CheckStaffIdList
    public void saveStaffSalaryInfo(List<String> staffIds, StaffSalaryEntity entity) {
        StaffPayrollInfo staffPayrollInfo = staffPayrollInfoRepository.findByCompanyIdAndStaffId(entity.getCompanyId(), entity.getStaffId());
        if (staffPayrollInfo == null) {
            SyncedBasicStaffInfo syncedBasicStaffInfo = syncedStaffInfoRepository.findByCompanyIdAndId(entity.getCompanyId(), entity.getStaffId());
            if (syncedBasicStaffInfo == null) {
                throw new Ihr360Exception(null, "薪资云中不存在该员工信息");
            }
            staffPayrollInfo = new StaffPayrollInfo();
            staffPayrollInfo.setStaff(syncedBasicStaffInfo);
            staffPayrollInfo.setCompanyId(entity.getCompanyId());
        }
        if (entity.getWorkHourType() == null) {
            throw new Ihr360Exception(null, "工时类型不可为空");
        }
        if (entity.getTaxRole() == null) {
            throw new Ihr360Exception(null, "纳税人身份不可为空");
        }
        StaffBankCard staffBankCard = new StaffBankCard();
        BeanUtils.copyProperties(entity, staffBankCard);
        staffPayrollInfo.setStaffBankCard(staffBankCard);
        staffPayrollInfo.setWorkHourType(entity.getWorkHourType());
        staffPayrollInfo.setTaxRole(entity.getTaxRole());
        staffPayrollInfo.setTaxExemption(entity.getTaxExemption());
        if (entity.getTaxDivisionCode() != null) {
            staffPayrollInfo.setTaxDivision(taxDivisionRepository.findByCode((entity.getTaxDivisionCode())));
        }
        staffPayrollInfoRepository.save(staffPayrollInfo);
    }

    @Override
    @CheckStaffIdList()
    public void saveAllStaffSalaryInfo(List<String> staffIds, String companyId, List<StaffBankSalaryEntity> staffBankSalaryEntityList) {
        if (CollectionUtils.isEmpty(staffBankSalaryEntityList)) {
            return;
        }
        List<StaffPayrollInfo> needSaveList = Lists.newArrayList();
        List<String> staffIdList = staffBankSalaryEntityList.stream().map(StaffBankSalaryEntity::getStaffId).collect(Collectors.toList());
        List<SyncedBasicStaffInfo> staffInfoList = syncedStaffInfoRepository.findByCompanyIdAndIdIn(companyId, staffIdList);
        Map<String, SyncedBasicStaffInfo> staffMap = staffInfoList.stream().collect(Collectors.toMap(SyncedBasicStaffInfo::getId, Function.identity()));
        List<StaffPayrollInfo> staffPayrollInfoList = staffPayrollInfoRepository.findByCompanyIdAndStaffIdIn(companyId, staffIdList);
        Map<String, StaffPayrollInfo> map = staffPayrollInfoList.stream().collect(Collectors.toMap(staff -> staff.getStaff().getId(), Function.identity()));
        for (StaffBankSalaryEntity staffBankSalaryEntity : staffBankSalaryEntityList) {
            StaffPayrollInfo payrollInfo = map.get(staffBankSalaryEntity.getStaffId());
            if (payrollInfo == null) {
                payrollInfo = new StaffPayrollInfo();
                SyncedBasicStaffInfo staffInfo = staffMap.get(staffBankSalaryEntity.getStaffId());
                if (staffInfo == null) {
                    continue;
                }
                payrollInfo.setStaff(staffInfo);
            }
            StaffBankCard staffBankCard = new StaffBankCard();
            BeanUtils.copyProperties(staffBankSalaryEntity, staffBankCard);
            payrollInfo.setStaffBankCard(staffBankCard);
            needSaveList.add(payrollInfo);
        }
        staffPayrollInfoRepository.save(needSaveList);
    }


    @Override
    public void downloadTemplate(ServletOutputStream outputStream) {
        ExportParams exportParams = new ExportParams();

        //表头
        Map<String, String> headerMap = new LinkedHashMap<>();
        exportParams.setHeaderMap(headerMap);
        headerMap.put(STAFF_SALARY_IMPORT_HEADER_KEY_NAME, importConstant.get(STAFF_SALARY_IMPORT_HEADER_KEY_NAME));
        headerMap.put(STAFF_SALARY_IMPORT_HEADER_KEY_MOBILE, importConstant.get(STAFF_SALARY_IMPORT_HEADER_KEY_MOBILE));
        headerMap.put(STAFF_SALARY_IMPORT_HEADER_KEY_EFFECTIVEAT, importConstant.get(STAFF_SALARY_IMPORT_HEADER_KEY_EFFECTIVEAT));
        headerMap.put(STAFF_SALARY_IMPORT_HEADER_KEY_ADJUST_RESSON, importConstant.get(STAFF_SALARY_IMPORT_HEADER_KEY_ADJUST_RESSON));

        //样式
        ExcelCellStyle requiredStyle = ExcelCellStyleFactory.createRequiredHeaderCellStyle();
        Map<String, ExcelCellStyle> headerStyleMap = new HashedMap();
        headerStyleMap.put(STAFF_SALARY_IMPORT_HEADER_KEY_NAME, requiredStyle);
        headerStyleMap.put(STAFF_SALARY_IMPORT_HEADER_KEY_EFFECTIVEAT, requiredStyle);
        headerStyleMap.put(STAFF_SALARY_IMPORT_HEADER_KEY_ADJUST_RESSON, requiredStyle);
        exportParams.setHeaderStyleMap(headerStyleMap);

        //下拉列　
        Map<String, List<String>> dropDownsMap = new LinkedHashMap<>();
        List<String> changeTypeList = new ArrayList<>();

        SalaryProfileChangeType[] changeTypes = SalaryProfileChangeType.values();
        for (SalaryProfileChangeType changeType : changeTypes) {
            changeTypeList.add(changeType.getTitle());
        }
        dropDownsMap.put(STAFF_SALARY_IMPORT_HEADER_KEY_ADJUST_RESSON, changeTypeList);
        exportParams.setDropDownsMap(dropDownsMap);


        CompanySalaryProfileEntity companySalaryProfileEntity = companySalaryProfileService.getByCompanyId(Ihr360SessionContextHolder.getCompanyId());
        List<SalaryProfileFieldEntity> salaryProfileFields = companySalaryProfileEntity.getProfileFields();
        if (CollectionUtils.isNotEmpty(salaryProfileFields)) {
            salaryProfileFields.forEach(salaryProfileField -> {
                headerMap.put(salaryProfileField.getCode(), salaryProfileField.getName());
            });
        }
        ExcelUtil.exportExcel(exportParams, outputStream);
    }

    @Override
    public void importSalaryProfile(ImportRequest importRequest) {
        StaffPayrollInfosRequest staffpayrollInfos = convertImportRequestToPayrollInfos(importRequest);
        importDataHandler(staffpayrollInfos);
        List<String> staffIds = Lists.newArrayList();
        List<StaffPayrollInfoRequest> staffPayrollInfoRequests = staffpayrollInfos.getPayrollInfos();
        if (CollectionUtils.isNotEmpty(staffPayrollInfoRequests)) {
            staffIds = staffPayrollInfoRequests.stream().map(StaffPayrollInfoRequest::getStaffId).collect(Collectors.toList());
        }

        ((StaffPayrollInfoService) AopContext.currentProxy()).adjustSalaryProfileWrapperNoValid(staffIds, staffpayrollInfos);
    }


    private StaffPayrollInfosRequest convertImportRequestToPayrollInfos(ImportRequest importRequest) {
        List<CorrectSpreadsheetRowEntity> spreadsheetRowEntities = importRequest.getCorrectDatas();
        StaffPayrollInfosRequest request = new StaffPayrollInfosRequest();
        if (CollectionUtils.isEmpty(spreadsheetRowEntities)) {
            return request;
        }
        List<StaffPayrollInfoRequest> payrollInfos = Lists.newArrayList();
        request.setPayrollInfos(payrollInfos);


        Map<String, List<CorrectSpreadsheetRowEntity>> spreadsheetRowsMap = spreadsheetRowEntities.stream().collect(Collectors.groupingBy(CorrectSpreadsheetRowEntity::getStaffId));
        AuthUserInfo authUserInfo = Ihr360SessionContextHolder.getSession().getUserInfo();

        CompanySalaryProfileEntity companySalaryProfile = companySalaryProfileService.getByCompanyId(Ihr360SessionContextHolder.getCompanyId());
        List<SalaryProfileFieldEntity> companySalaryprofiles = companySalaryProfile.getProfileFields();

        Map<String, String> companySalaryProfileMap = companySalaryprofiles.stream().collect(Collectors.toMap(SalaryProfileFieldEntity::getName, SalaryProfileFieldEntity::getCode));

        spreadsheetRowsMap.forEach((staffId, rowEntityes) -> {
            StaffPayrollInfoRequest staffPayrollInfoRequest = new StaffPayrollInfoRequest();
            payrollInfos.add(staffPayrollInfoRequest);
            staffPayrollInfoRequest.setStaffId(staffId);
            staffPayrollInfoRequest.setUserName(authUserInfo.getUserName());
            staffPayrollInfoRequest.setUserId(authUserInfo.getUserId());
            staffPayrollInfoRequest.setCompanyId(authUserInfo.getCompanyId());

            List<SalaryProfileTypeItem> typeItems = Lists.newArrayList();
            staffPayrollInfoRequest.setTypeItems(typeItems);

            for (CorrectSpreadsheetRowEntity rowEntity : rowEntityes) {
                SalaryProfileTypeItem typeItem = new SalaryProfileTypeItem();
                typeItems.add(typeItem);
                Map<String, Object> rowData = rowEntity.getData();
                SalaryProfileChangeType changeType = SalaryProfileChangeType.getChangeType((String) rowData.get(importConstant.get(STAFF_SALARY_IMPORT_HEADER_KEY_ADJUST_RESSON)));
//                Date effectiveAt = DateUtils.parse)));

                Date effectiveAt = null;
                try {
                    Object effectiveAtObj = rowData.get(importConstant.get(STAFF_SALARY_IMPORT_HEADER_KEY_EFFECTIVEAT));
                    if (effectiveAtObj instanceof Date) {
                        effectiveAt = (Date) effectiveAtObj;
                    } else {
                        effectiveAt = ObjectMapperWrapper.INSTANCE.getObjectMapper().getDateFormat().parse(String.valueOf(rowData.get(importConstant.get(STAFF_SALARY_IMPORT_HEADER_KEY_EFFECTIVEAT))));
                    }
                } catch (ParseException e) {
                    throw new Ihr360Exception(null, "生效时间数据类型错误，请检查！");
                }

                typeItem.setChangeType(changeType);
                typeItem.setRowNum(rowEntity.getRowNum());
                typeItem.setEffectiveAt(effectiveAt);
                List<SalaryProfileFieldRequest> profileFields = Lists.newArrayList();
                typeItem.setProfileFields(profileFields);

                for (Map.Entry<String, Object> stringObjectEntry : rowData.entrySet()) {
                    String header = stringObjectEntry.getKey();
                    Object value = stringObjectEntry.getValue();
                    if (importConstant.values().contains(header)) {
                        continue;
                    }
                    if (StringUtils.isEmpty(companySalaryProfileMap.get(header))) {
                        continue;
                    }
                    SalaryProfileFieldRequest salaryProfileFieldRequest = new SalaryProfileFieldRequest();
                    salaryProfileFieldRequest.setName(header);
                    if (value != null) {
                        salaryProfileFieldRequest.setValue(String.valueOf(value));
                    }
                    salaryProfileFieldRequest.setCode(companySalaryProfileMap.get(header));
                    salaryProfileFieldRequest.setEffectiveAt(effectiveAt);
                    salaryProfileFieldRequest.setChangeType(changeType);
                    salaryProfileFieldRequest.setAdjustManually(true);
                    profileFields.add(salaryProfileFieldRequest);
                }
            }
        });
        return request;
    }

    /**
     * 处理导入数据
     * 删除（同一员工）导入数据最早生效时间之后的数据
     *
     * @param target
     * @param
     */

    private void importDataHandler(StaffPayrollInfosRequest target) {

        Set<String> staffIds = target.getPayrollInfos().stream().map(StaffPayrollInfoRequest::getStaffId).collect(Collectors.toSet());
        List<StaffSalaryProfileEntity> existStaffSalaryProfiles = getSomeStaffsPayrollInfos(Lists.newArrayList(staffIds));

        //本次请求的员工薪资数据
        Map<String, StaffPayrollInfoRequest> payrollInfosMap = target.getPayrollInfos().stream().collect(Collectors.toMap(StaffPayrollInfoRequest::getStaffId, Function.identity()));

        if (CollectionUtils.isNotEmpty(existStaffSalaryProfiles)) {
            //已经存在的员工薪资数据
            Map<String, List<StaffSalaryProfileEntity>> existStaffIdProfileMap = existStaffSalaryProfiles.stream().collect(Collectors.groupingBy(s -> s.getStaff().getId()));

            existStaffIdProfileMap.forEach((staffId, profiles) -> {

                StaffPayrollInfoRequest requestProfileSalary = payrollInfosMap.get(staffId);
                Date minRequestEffectiveDate = null;
                if (requestProfileSalary != null && CollectionUtils.isNotEmpty(requestProfileSalary.getTypeItems())) {
                    minRequestEffectiveDate = requestProfileSalary.getTypeItems().stream().map(SalaryProfileTypeItem::getEffectiveAt).min(Comparator.naturalOrder()).get();
                    minRequestEffectiveDate = DateUtils.getMaxDateInDay(minRequestEffectiveDate);
                }

                StaffPayrollInfoRequest staffPayrollInfoRequest = new StaffPayrollInfoRequest();

                staffPayrollInfoRequest.setStaffId(staffId);
                List<SalaryProfileTypeItem> typeItems = Lists.newArrayList();
                staffPayrollInfoRequest.setTypeItems(typeItems);

                for (StaffSalaryProfileEntity profile : profiles) {

                    //如果是同一天，则覆盖所有薪资项目
                    boolean isSameDay = minRequestEffectiveDate != null && DateUtils.isSameDay(profile.getSalaryProfile().getEffectiveAt(), minRequestEffectiveDate);
                    if (isSameDay) {
                        for (SalaryProfileTypeItem item : requestProfileSalary.getTypeItems()) {
                            if (!DateUtils.isSameDay(item.getEffectiveAt(), minRequestEffectiveDate)) {
                                continue;
                            }
                            List<SalaryProfileFieldRequest> requestProfileFields = item.getProfileFields();
                            Map<String, SalaryProfileFieldRequest> requestProfieMap = requestProfileFields.stream().collect(Collectors.toMap(s -> s.getCode(), Function.identity()));

                            Map<String, DynamicCell> exitstItems = profile.getSalaryProfile().getWrapper().getItems();
                            exitstItems.forEach((code, cell) -> {
                                SalaryProfileFieldRequest salaryProfileFieldRequest = requestProfieMap.get(code);
                                if (salaryProfileFieldRequest == null) {
                                    salaryProfileFieldRequest = new SalaryProfileFieldRequest();
                                    salaryProfileFieldRequest.setChangeType(item.getChangeType());
                                    salaryProfileFieldRequest.setAdjustManually(true);
                                    salaryProfileFieldRequest.setEffectiveAt(item.getEffectiveAt());
                                    salaryProfileFieldRequest.setCode(code);
                                    salaryProfileFieldRequest.setValue(null);
                                    requestProfileFields.add(salaryProfileFieldRequest);
                                }
                            });
                        }
                        continue;
                    }

                    //删除导入数据最早生效时间之后的数据
                    boolean isSameOrAfterProfile = minRequestEffectiveDate != null && (DateUtils.isSameDay(profile.getSalaryProfile().getEffectiveAt(), minRequestEffectiveDate) || minRequestEffectiveDate.before(profile.getSalaryProfile().getEffectiveAt()));
                    if (isSameOrAfterProfile) {
                        continue;
                    }

                    SalaryProfileTypeItem rowItem = new SalaryProfileTypeItem();
                    typeItems.add(rowItem);

                    SalaryProfileEntity salaryProfileEntity = profile.getSalaryProfile();
                    Map<String, DynamicCell> items = salaryProfileEntity.getWrapper().getItems();
                    if (MapUtils.isEmpty(items)) {
                        continue;
                    }
                    rowItem.setChangeType(salaryProfileEntity.getChangeType());
                    rowItem.setEffectiveAt(salaryProfileEntity.getEffectiveAt());
                    List<SalaryProfileFieldRequest> rowSalaryProfileFields = Lists.newArrayList();
                    rowItem.setProfileFields(rowSalaryProfileFields);

                    items.forEach((code, dynamicCell) -> {
                        SalaryProfileFieldRequest salaryProfileFieldRequest = new SalaryProfileFieldRequest();
                        rowSalaryProfileFields.add(salaryProfileFieldRequest);
                        salaryProfileFieldRequest.setChangeType(salaryProfileEntity.getChangeType());
                        salaryProfileFieldRequest.setEffectiveAt(salaryProfileEntity.getEffectiveAt());
                        salaryProfileFieldRequest.setCode(code);
                        salaryProfileFieldRequest.setValue(dynamicCell.getCellValue());
                    });
                }
                if (CollectionUtils.isNotEmpty(typeItems)) {
                    StaffPayrollInfoRequest request = payrollInfosMap.get(staffId);
                    if (request == null) {
                        request = staffPayrollInfoRequest;
                        payrollInfosMap.put(staffId, request);
                    }
                    request.getTypeItems().addAll(typeItems);
                }

            });
        }
    }

    /**
     * 得到需要保存的数据
     *
     * @param toSave
     * @param requestCodeDateMap
     * @param staffSalaryProfile
     * @param sortedEffectiveDates
     */

    private void handleToSaveDate(final List<StaffSalaryProfile> toSave, final Map<String, Map<Date, SalaryProfileFieldRequest>> requestCodeDateMap,
                                  final StaffSalaryProfile staffSalaryProfile, final List<Date> sortedEffectiveDates) {
        for (int i = 0; i < sortedEffectiveDates.size(); i++) {
            Date nextEffectiveDate = sortedEffectiveDates.get(i);
            Date nextInvalidDate = null;
            if (i != sortedEffectiveDates.size() - 1) {
                nextInvalidDate = DateUtils.subDays(sortedEffectiveDates.get(i + 1), 1);
            }
            StaffSalaryProfile staffPayrollInfo = new StaffSalaryProfile();
            staffPayrollInfo.setStaff(staffSalaryProfile.getStaff());
//            staffPayrollInfo.setWorkHourType(); todo
            staffPayrollInfo.setCompanyId(staffSalaryProfile.getCompanyId());
            staffPayrollInfo.setCreatedAt(new Date());
            SalaryProfile salaryProfile = new SalaryProfile();
            staffPayrollInfo.setSalaryProfile(salaryProfile);
            salaryProfile.setEffectiveAt(nextEffectiveDate);
            salaryProfile.setInValidAt(nextInvalidDate);
            salaryProfile.setCompanySalaryProfile(staffSalaryProfile.getSalaryProfile().getCompanySalaryProfile());
            SalaryProfileItemsWrapper wrapper = new SalaryProfileItemsWrapper();
            salaryProfile.setWrapper(wrapper);
            Map<String, DynamicCell> items = new HashMap<>();
            wrapper.setItems(items);
            SalaryProfileChangeType changeType = handleSalaryProfileValue(requestCodeDateMap, nextEffectiveDate, items);
            salaryProfile.setChangeType(changeType);
            toSave.add(staffPayrollInfo);
        }
    }

    /**
     * 获取当前时间段薪资档案所有薪资项的值
     *
     * @param requestCodeDateMap Map<code,Map<effectiveAt,value>>
     * @param nextEffectiveDate
     * @param items
     */
    private SalaryProfileChangeType handleSalaryProfileValue(Map<String, Map<Date, SalaryProfileFieldRequest>> requestCodeDateMap, Date nextEffectiveDate, final Map<String, DynamicCell> items) {
        final SalaryProfileChangeType[] resultType = {null};
        final Date[] typeDate = {null};
        requestCodeDateMap.forEach((code, dateValueMap) -> {
            final String[] dynamicCellValue = {null};
            final Date[] tempDate = {null};
            //取生效时间小于等于当前生效时间的数据里最大的那条的数据（就近）
            dateValueMap.forEach((effectiveAt, field) -> {
                if (effectiveAt.before(nextEffectiveDate)
                        || DateUtils.isSameDay(effectiveAt, nextEffectiveDate)) {
                    dynamicCellValue[0] = tempDate[0] == null || tempDate[0].before(effectiveAt) ? field.getValue() : dynamicCellValue[0];
                    resultType[0] = typeDate[0] == null || typeDate[0].before(effectiveAt) ? field.getChangeType() : resultType[0];

                    tempDate[0] = tempDate[0] == null || tempDate[0].before(effectiveAt) ? effectiveAt : tempDate[0];
                    typeDate[0] = typeDate[0] == null || typeDate[0].before(effectiveAt) ? effectiveAt : typeDate[0];
                }
            });
            if (dynamicCellValue[0] != null) {
                items.put(code, new DynamicCell(dynamicCellValue[0]));
            }
        });
        return resultType[0];
    }

    /**
     * 调整薪资档案数据前，校验数据的准确性
     *
     * @param staffPayrollInfoMap
     */
    private void checkStaffpayrollInfoValid(final Map<String, List<StaffSalaryProfile>> staffPayrollInfoMap) {
        staffPayrollInfoMap.forEach((staffId, infos) -> {
            SyncedBasicStaffInfo currentStaff = infos.get(0).getStaff();
            //是否存在多条失效日期为空的记录
            checkNullInvalidAtMultiple(infos, currentStaff);
            //校验当前生效数据
            checkEffecPayrollInfo(infos, currentStaff);
            //校验时间有效性
            checkDateValid(infos, currentStaff);

        });
    }

    /**
     * 校验时间的有效性
     *
     * @param infos
     * @param currentStaff
     */
    private void checkDateValid(List<StaffSalaryProfile> infos, SyncedBasicStaffInfo currentStaff) {
        Date minEffectiveDate = null;
        Date maxInvalidDate = null;
        int termDays = 0;
        StaffSalaryProfile lastStaffPayrollInfo = null;
        for (StaffSalaryProfile info : infos) {
            Date effectiveDate = DateUtils.dateFormat(info.getSalaryProfile().getEffectiveAt(), DateUtils.STANDARD_DATE_FORMAT_YMD);
            Date invalidDate = info.getSalaryProfile().getInValidAt() != null
                    ? DateUtils.dateFormat(info.getSalaryProfile().getInValidAt(), DateUtils.STANDARD_DATE_FORMAT_YMD)
                    : null;

            minEffectiveDate = (minEffectiveDate == null || minEffectiveDate.after(effectiveDate) ? effectiveDate : minEffectiveDate);
            maxInvalidDate = maxInvalidDate == null || (invalidDate != null && maxInvalidDate.before(invalidDate)) ? invalidDate : maxInvalidDate;

            if (invalidDate != null) {
                termDays += (DateUtils.DateDiff(DateUtils.getMinDateInDay(effectiveDate), DateUtils.getMinDateInDay(invalidDate)) + 1);
            }
            if (info.getSalaryProfile().getInValidAt() == null) {
                lastStaffPayrollInfo = info;
            }
        }
        if (maxInvalidDate != null) {
            int betweenDays = DateUtils.DateDiff(DateUtils.getMinDateInDay(minEffectiveDate), DateUtils.getMinDateInDay(maxInvalidDate)) + 1;
            boolean dateConflict = betweenDays != termDays || !DateUtils.isSameDay(lastStaffPayrollInfo.getSalaryProfile().getEffectiveAt(), DateUtils.addDays(maxInvalidDate, 1));
            if (dateConflict) {
                throw new Ihr360Exception(null,
                        String.join("", currentStaff.getStaffName(), "数据异常，薪资档案生效日期和失效日期存在冲突记录！"));
            }
        }
    }

    /**
     * 校验生效的薪资档案
     *
     * @param infos
     * @param currentStaff
     */
    private void checkEffecPayrollInfo(List<StaffSalaryProfile> infos, SyncedBasicStaffInfo currentStaff) {
        List<StaffSalaryProfile> effectiveStaffPayrollInfos = infos.stream().filter(StaffSalaryProfile::isCurrentEffect).collect(Collectors.toList());
        ValidationUtils.rejectIfEmpty(effectiveStaffPayrollInfos, null, String.join("", currentStaff.getStaffName(), "不存在生效的薪资档案！"));
        ValidationUtils.rejectGt(Long.valueOf(effectiveStaffPayrollInfos.size()).intValue(), 1, null, String.join("", currentStaff.getStaffName(), "当前存在多条生效的薪资档案！"));
    }

    /**
     * 校验是否存在多条失效日期为空的记录
     *
     * @param infos
     * @param currentStaff
     */
    private void checkNullInvalidAtMultiple(List<StaffSalaryProfile> infos, SyncedBasicStaffInfo currentStaff) {
        long nullEffectiveCount = infos.stream().filter(s -> s.getSalaryProfile().getInValidAt() == null).count();
        ValidationUtils.rejectGt(Long.valueOf(nullEffectiveCount).intValue(), 1, null,
                String.join("", "数据异常，", currentStaff.getStaffName(), "存在多条失效日期为空的记录！"));
    }

    /**
     * 校验薪资档案数据是否存在
     *
     * @param payrollInfos
     * @param requestStaffIds
     * @param existStaffPayrllInfos
     */
    private void checkExistStaffPayrollInfo(final List<StaffPayrollInfoRequest> payrollInfos, final Set<String> requestStaffIds, final List<StaffSalaryProfile> existStaffPayrllInfos) {

        ValidationUtils.rejectIfEmpty(existStaffPayrllInfos, null, "员工不存在薪资档案数据！");
        Set<String> existStaffIds = existStaffPayrllInfos.stream().map(s -> s.getStaff().getId()).collect(Collectors.toSet());
        requestStaffIds.removeAll(existStaffIds);
        if (requestStaffIds.size() > 0) {
            Set<String> staffNames = payrollInfos.stream()
                    .filter(p -> requestStaffIds.contains(p.getStaffId()))
                    .map(StaffPayrollInfoRequest::getStaffName)
                    .collect(Collectors.toSet());
            StringJoiner joiner = new StringJoiner(",");
            joiner.add("员工：");
            staffNames.forEach(name -> {
                joiner.add(name);
            });
            joiner.add("不存在薪资档案数据");
            ValidationUtils.rejectIfEmpty(staffNames, null, joiner.toString());
        }

//        Map<String, SalaryProfileChangeType> reqStaffMonthTypeMap = Maps.newHashMap();
//        payrollInfos.stream().forEach(reqPayrollInfo -> {
//            String StaffId = reqPayrollInfo.getStaffId();
//            reqPayrollInfo.getTypeItems().forEach(item -> {
//                String monthStr = DateUtils.formatDateStr(item.getEffectiveAt(), DateUtils.STANDARD_DATE_FORMAT_YM1);
//                reqStaffMonthTypeMap.put(String.join("", StaffId, monthStr), item.getChangeType());
//            });
//        });
//
//        //校验当月是否已存在
//        Map<String, List<StaffPayrollInfo>> monthTypeMap = existStaffPayrllInfos.stream()
//                .collect(Collectors.groupingBy(s -> String.join("",
//                        s.getStaff().getId(),
//                        DateUtils.formatDateStr(s.getSalaryProfile().getEffectiveAt(), DateUtils.STANDARD_DATE_FORMAT_YM1))));
//        monthTypeMap.forEach((month, types) -> {
//            if (types.contains(reqStaffMonthTypeMap.get(month))) {
//                throw new Ihr360Exception(null, String.join("", reqStaffMonthTypeMap.get(month).getTitle(), "(", month, ")", "已存在调整记录"));
//            }
//            ;
//        });

    }

    /**
     * 获取每个员工的大于当前生效时间的所有记录
     *
     * @param companyId
     * @param staffEffectiveMap Map<staffId,effectiveAt>
     * @return
     */
    private Specification<StaffSalaryProfile> getStaffIdAfterEffectiveAt(String companyId, Map<String, Date> staffEffectiveMap) {
        if (MapUtils.isEmpty(staffEffectiveMap)) {
            throw new Ihr360RuntimeException("参数异常，toDelete不能为空！");
        }
        return (Root<StaffSalaryProfile> root, CriteriaQuery<?> q, CriteriaBuilder cb) -> {
            Predicate predicate = cb.conjunction();
            predicate.getExpressions().add(cb.equal(root.get(ICompanyId.Field.COMPANY_ID).as(String.class), companyId));
            Expression<String> staffId = JpaCriteriaPathUtils.getPath(root, StaffSalaryProfile.Field.STAFF_ID).as(String.class);
            Expression<Date> effectiveAt = JpaCriteriaPathUtils.getPath(root, StaffSalaryProfile.Field.EFFECTIVE_AT).as(Date.class);

            if (MapUtils.isNotEmpty(staffEffectiveMap)) {
                List<Predicate> staffEffectivePredicates = Lists.newArrayList();
                staffEffectiveMap.forEach((id, effective) -> {
                    Predicate staffEqualsPredicate = cb.equal(staffId, id);
                    Predicate effectiveAtPredicate = cb.greaterThan(effectiveAt, effective);
                    staffEffectivePredicates.add(cb.or(cb.and(staffEqualsPredicate, effectiveAtPredicate)));
                });

                Predicate p = cb.or(staffEffectivePredicates.toArray(new Predicate[staffEffectivePredicates.size()]));
                predicate.getExpressions().add(p);
            }
            q.where(predicate);
            return q.getRestriction();
        };
    }

    /**
     * 生成员工薪资档案调整的日志
     *
     * @param companyId
     * @param requestPayrollInfos
     * @param notInit             是否时数据修复初始化数据
     * @return
     */
    private List<StaffPayrollInfoJournal> generateStaffPayrollInfoJournals(String companyId, List<StaffPayrollInfoRequest> requestPayrollInfos, boolean notInit) {

        List<StaffPayrollInfoJournal> journals = Lists.newArrayList();
        List<String> staffIds = requestPayrollInfos.stream().map(StaffPayrollInfoRequest::getStaffId).collect(Collectors.toList());

        List<StaffPayrollInfoJournal> existJournals = Lists.newArrayList();
        //todo 为什么只有adjustManually时
        if (notInit) {
            existJournals = staffPayrollInfoJournalRepository.findByCompanyIdAndStaffIdInAndIsDeleted(companyId, staffIds, false);
        }

        Map<String, List<StaffPayrollInfoJournal>> existStaffJournalMap = Maps.newHashMap();
        if (CollectionUtils.isNotEmpty(existJournals)) {
            existStaffJournalMap = existJournals.stream().collect(Collectors.groupingBy(StaffPayrollInfoJournal::getStaffId));
        }
        Map<String, List<StaffPayrollInfoJournal>> finalExistStaffJournalMap = existStaffJournalMap;

        List<StaffPayrollInfoJournal> toMarkRemoveJournals = Lists.newArrayList();
        requestPayrollInfos.forEach(payrollInfo -> {
            String staffId = payrollInfo.getStaffId();
            List<SalaryProfileTypeItem> salaryProfileItems = payrollInfo.getTypeItems();
            Map<String, List<SalaryProfileFieldRequest>> salaryProfileFieldMap = salaryProfileItems.stream()
                    .map(SalaryProfileTypeItem::getProfileFields)
                    .flatMap(Collection::stream)
                    .collect(Collectors.groupingBy(SalaryProfileFieldRequest::getCode));

            List<StaffPayrollInfoJournal> staffJournals = finalExistStaffJournalMap.get(staffId);
            Map<String, List<StaffPayrollInfoJournal>> codeJournalMap = Maps.newHashMap();
            if (CollectionUtils.isNotEmpty(staffJournals)) {
                codeJournalMap = handleRemoveJournals(toMarkRemoveJournals, salaryProfileItems, staffJournals);
            }

            Map<String, List<StaffPayrollInfoJournal>> finalCodeJournalMap = codeJournalMap;

            salaryProfileFieldMap.forEach((code, profileFields) -> {
                profileFields.sort(Comparator.comparing(SalaryProfileFieldRequest::getEffectiveAt));
                List<StaffPayrollInfoJournal> codeJournals = finalCodeJournalMap.get(code);
                Map<String, List<StaffPayrollInfoJournal>> effectiveJournalMap = Maps.newHashMap();
                if (CollectionUtils.isNotEmpty(codeJournals)) {
                    effectiveJournalMap = codeJournals.stream().collect(Collectors.groupingBy(j -> DateUtils.formatDateStr(j.getEffectiveAt(), DateUtils.STANDARD_DATE_FORMAT_YMD)));
                }
                handleJournals(notInit, journals, payrollInfo, profileFields, effectiveJournalMap);
            });
        });
        if (CollectionUtils.isNotEmpty(toMarkRemoveJournals)) {
            toMarkRemoveJournals.forEach(journal -> {
                journal.setDeleted(true);
                journal.setUpdateAt(new Date());
            });
            journals.addAll(toMarkRemoveJournals);
        }
        return journals;
    }

    private void handleJournals(boolean notInit, List<StaffPayrollInfoJournal> journals, StaffPayrollInfoRequest payrollInfo, List<SalaryProfileFieldRequest> profileFields, Map<String, List<StaffPayrollInfoJournal>> effectiveJournalMap) {
        for (int i = 0; i < profileFields.size(); i++) {
            SalaryProfileFieldRequest salaryProfile = profileFields.get(i);
            String oldValue = null;
            SalaryProfileChangeType oldChangeType = null;
            if (i == 0) {
                oldValue = null;
            } else {

                SalaryProfileFieldRequest oldSalaryProfile = profileFields.get(i - 1);
                oldChangeType = oldSalaryProfile.getChangeType();
                oldValue = oldSalaryProfile.getValue();
            }
            boolean noneChange = ((StringUtils.isEmpty(oldValue) && StringUtils.isEmpty(salaryProfile.getValue())) ||
                    (StringUtils.isNotEmpty(oldValue) && oldValue.equals(salaryProfile.getValue())));
//                    && (oldChangeType == null || oldChangeType == salaryProfile.getChangeType());
            if (noneChange) {
                continue;
            }

            List<StaffPayrollInfoJournal> effectiveJournals = effectiveJournalMap.get(DateUtils.formatDateStr(salaryProfile.getEffectiveAt(), DateUtils.STANDARD_DATE_FORMAT_YMD));
            if (CollectionUtils.isNotEmpty(effectiveJournals) && notInit) {
                //取最后一次修改的值
                Map<SalaryProfileChangeType, List<StaffPayrollInfoJournal>> typeJournalMap = effectiveJournals.stream()
                        .collect(Collectors.groupingBy(StaffPayrollInfoJournal::getChangeType));
                List<StaffPayrollInfoJournal> typeJournals = typeJournalMap.get(salaryProfile.getChangeType());
                if (CollectionUtils.isNotEmpty(typeJournals)) {
                    typeJournals.sort((e1, e2) -> e2.getCreatedAt().compareTo(e1.getCreatedAt()));
                    StaffPayrollInfoJournal effectiveJournal = typeJournals.get(0);
                    String exitOldValue = effectiveJournal.getOldValue();
                    String exitNewValue = effectiveJournal.getNewValue();
                    if (StringUtils.isEmpty(oldValue)) {
                        oldValue = exitOldValue;
                    }
                    boolean sameOldValue = (StringUtils.isEmpty(exitOldValue) && StringUtils.isEmpty(oldValue))
                            || (StringUtils.isNotEmpty(exitOldValue) && exitOldValue.equals(oldValue));
                    boolean sameNewValue = (StringUtils.isEmpty(exitNewValue) && StringUtils.isEmpty(salaryProfile.getValue()))
                            || (StringUtils.isNotEmpty(exitNewValue) && exitNewValue.equals(salaryProfile.getValue()));

                    if (StringUtils.isNotEmpty(exitNewValue) && oldValue == null) {
                        oldValue = exitNewValue;
                    }
                    if (sameNewValue && sameOldValue) {
                        continue;
                    }
                }
            }
            StaffPayrollInfoJournal journal = getStaffPayrollInfoJournal(payrollInfo, salaryProfile, oldValue);
            journals.add(journal);
        }
    }

    private StaffPayrollInfoJournal getStaffPayrollInfoJournal(StaffPayrollInfoRequest payrollInfo, SalaryProfileFieldRequest salaryProfile, String oldValue) {
        String companyId = Ihr360SessionContextHolder.getCompanyId();
        if (StringUtils.isEmpty(companyId)) {
            companyId = payrollInfo.getCompanyId();
        }
        AuthUserInfo user = Ihr360SessionContextHolder.getSession().getUserInfo();

        String userName = payrollInfo.getUserName();
        String userId = payrollInfo.getUserId();
        if (StringUtils.isEmpty(userName) && user != null) {
            userName = user.getUserName();
        }
        if (StringUtils.isEmpty(userId) && user != null) {
            userId = user.getUserId();
        }
        StaffPayrollInfoJournal journal = new StaffPayrollInfoJournal();
        journal.setStaffId(payrollInfo.getStaffId());
        journal.setCompanyId(companyId);
        journal.setStaffId(payrollInfo.getStaffId());
        journal.setCreatedAt(new Date());
        journal.setFieldCode(salaryProfile.getCode());
        journal.setFieldName(salaryProfile.getName());
        journal.setOldValue(oldValue);
        journal.setNewValue(salaryProfile.getValue());
        journal.setProcessorId(userId);
        journal.setProcessorName(userName);
        journal.setChangeType(salaryProfile.getChangeType());
        journal.setEffectiveAt(salaryProfile.getEffectiveAt());
        journal.setCreatedAt(new Date());
        journal.setAdjustManually(salaryProfile.getAdjustManually());
        return journal;
    }

    private Map<String, List<StaffPayrollInfoJournal>> handleRemoveJournals(List<StaffPayrollInfoJournal> toMarkRemoveJournals, List<SalaryProfileTypeItem> salaryProfileItems, List<StaffPayrollInfoJournal> staffJournals) {
        Set<String> efftiveAtTypes = salaryProfileItems.stream()
                .map(s -> DateUtils.formatDateStr(s.getEffectiveAt(), DateUtils.STANDARD_DATE_FORMAT_YMD) + s.getChangeType().toString())
                .collect(Collectors.toSet());

        Map<String, SalaryProfileFieldRequest> falaryProfileMap = salaryProfileItems.stream()
                .map(SalaryProfileTypeItem::getProfileFields).flatMap(f -> f.stream())
                .collect(Collectors.toMap(f -> DateUtils.formatDateStr(f.getEffectiveAt(), DateUtils.STANDARD_DATE_FORMAT_YMD)
                                + f.getChangeType().toString() + f.getCode() + f.getValue()
                        , Function.identity()));

        Map<String, List<StaffPayrollInfoJournal>> journalMap = staffJournals.stream()
                .collect(Collectors.groupingBy(j -> DateUtils.formatDateStr(j.getEffectiveAt(), DateUtils.STANDARD_DATE_FORMAT_YMD) + j.getChangeType() + j.getFieldCode() + j.getNewValue()));


        List<StaffPayrollInfoJournal> removeJournals = staffJournals.stream()
                .filter(j -> !efftiveAtTypes.contains(DateUtils.formatDateStr(j.getEffectiveAt(), DateUtils.STANDARD_DATE_FORMAT_YMD) + j.getChangeType().toString()))
                .collect(Collectors.toList());

        Set<String> journalIds = removeJournals.stream().map(StaffPayrollInfoJournal::getId).collect(Collectors.toSet());
        toMarkRemoveJournals.addAll(removeJournals);


        Map<String, List<StaffPayrollInfoJournal>> codeJournalMap = staffJournals.stream()
                .filter(j -> !journalIds.contains(j.getId()))
                .collect(Collectors.groupingBy(StaffPayrollInfoJournal::getFieldCode));
        return codeJournalMap;
    }

    /**
     * 查询出 周期范围内的分段薪资数据
     * <p>
     * case 1:
     * ------------------->startOn+---------------------+endOn
     * ------->effectiveAt +------------+InValidAt
     * -------------->effectiveAt +---------------------+InValidAt
     * ------->effectiveAt +---------------------------------+InValidAt
     * ------->effectiveAt +-----------------------------------------------+max
     * <p>
     * case 2:
     * ------------------->startOn+---------------------+endOn
     * --------------------->effectiveAt +------------+InValidAt
     * --------------------->effectiveAt +-------------------+InValidAt
     * <p>
     **/
    //获取某个时间范围内的有效期
    private Specification<StaffSalaryProfile> getPeriodSpecification(String companyId, DateRange dateRange, Collection<String> staffIds) {
        return (root, q, cb) -> {
            Predicate predicate = cb.conjunction();
            predicate.getExpressions().add(cb.equal(root.get(ICompanyId.Field.COMPANY_ID).as(String.class), companyId));
            Expression<String> staffId = JpaCriteriaPathUtils.getPath(root, StaffSalaryProfile.Field.STAFF_ID).as(String.class);
            if (CollectionUtils.isNotEmpty(staffIds)) {
                Predicate in = staffId.in(staffIds);
                cb.in(in);
                predicate.getExpressions().add(in);
            }
            Predicate case1 = cb.conjunction();
            Expression<Date> inValidAt = JpaCriteriaPathUtils.getPath(root, StaffSalaryProfile.Field.INVALID_AT).as(Date.class);
            Expression<Date> effectiveAt = JpaCriteriaPathUtils.getPath(root, StaffSalaryProfile.Field.EFFECTIVE_AT).as(Date.class);
            case1.getExpressions().addAll(Lists.newArrayList(
                    cb.lessThanOrEqualTo(effectiveAt, dateRange.getStart()),
                    cb.or(cb.isNull(inValidAt), cb.greaterThanOrEqualTo(inValidAt, dateRange.getStart()))
                    )
            );

            Predicate case2 = cb.conjunction();
            case2.getExpressions().addAll(Lists.newArrayList(
                    cb.greaterThanOrEqualTo(effectiveAt, dateRange.getStart()),
                    cb.lessThanOrEqualTo(effectiveAt, dateRange.getEnd()),
                    cb.or(cb.isNull(inValidAt), cb.greaterThanOrEqualTo(inValidAt, effectiveAt))
                    )
            );

            q.where(cb.and(predicate, cb.or(case1, case2)));
            q.orderBy(cb.asc(staffId), cb.asc(inValidAt));
            return q.getRestriction();
        };
    }

    private SalaryProfileTypeItem convertSalaryProfileTosalaryProfileTypeItem(SalaryProfile salaryProfile) {
        SalaryProfileTypeItem salaryProfileTypeItem = new SalaryProfileTypeItem();
        salaryProfileTypeItem.setChangeType(salaryProfile.getChangeType());
        salaryProfileTypeItem.setEffectiveAt(salaryProfile.getEffectiveAt());
        List<SalaryProfileFieldRequest> profileFields = Lists.newArrayList();
        salaryProfileTypeItem.setProfileFields(profileFields);
        Map<String, DynamicCell> items = salaryProfile.getWrapper().getItems();
        items.forEach((code, dynamicell) -> {
            SalaryProfileFieldRequest salaryProfileFieldRequest = new SalaryProfileFieldRequest();
            salaryProfileFieldRequest.setCode(code);
            salaryProfileFieldRequest.setValue(dynamicell.getCellValue());
            salaryProfileFieldRequest.setChangeType(salaryProfile.getChangeType());
            salaryProfileFieldRequest.setEffectiveAt(salaryProfile.getEffectiveAt());
            profileFields.add(salaryProfileFieldRequest);
        });
        return salaryProfileTypeItem;
    }

    private void handlePayrollInfoData(StaffSalaryProfile staffSalaryProfile, List<StaffSalaryProfile> toSave, Set<Date> effectiveDates, List<SalaryProfileTypeItem> requestSalaryProfileItems) {
        List<SalaryProfileFieldRequest> allRequestSalaryProfileField = Lists.newArrayList();

        requestSalaryProfileItems.forEach(fieldItem -> {
            fieldItem.getProfileFields().forEach(field -> {
                field.setChangeType(fieldItem.getChangeType());
                field.setEffectiveAt(fieldItem.getEffectiveAt());
                allRequestSalaryProfileField.add(field);
            });
        });

        Map<String, List<SalaryProfileFieldRequest>> codeFieldMap = allRequestSalaryProfileField.stream()
                .collect(Collectors.groupingBy(SalaryProfileFieldRequest::getCode));

        Map<String, Map<Date, SalaryProfileFieldRequest>> requestCodeDateMap = Maps.newHashMap();
        codeFieldMap.forEach((code, salaryProfileField) -> {
            Map<Date, SalaryProfileFieldRequest> dateFieldMap = salaryProfileField.stream()
                    .collect(Collectors.toMap(SalaryProfileFieldRequest::getEffectiveAt, Function.identity()));

            effectiveDates.addAll(dateFieldMap.keySet());
            requestCodeDateMap.put(code, dateFieldMap);
        });

        List<Date> sortedEffectiveDates = new ArrayList<>(effectiveDates);
        sortedEffectiveDates.sort(Comparator.naturalOrder());
        handleToSaveDate(toSave, requestCodeDateMap, staffSalaryProfile, sortedEffectiveDates);
    }

    private void setSalaryfieldName(CompanySalaryProfile companySalaryProfile) {
        ValidationUtils.rejectIfEmpty(companySalaryProfile, null, "公司薪资档案不存在!");
        List<SalaryProfileField> salaryProfileFields = companySalaryProfile.getProfileFields();
        if (CollectionUtils.isNotEmpty(salaryProfileFields)) {
            List<String> codes = salaryProfileFields.stream().map(SalaryProfileField::getCode).collect(Collectors.toList());
            List<SystemSalaryField> systemSalaryFields = systemSalaryFieldRepository.findByCodeIn(codes);
            if (CollectionUtils.isEmpty(systemSalaryFields)) {
                logger.error("数据异常，根据code查询不到系统薪资字段！code:{}", JSONObject.toJSONString(codes));
                throw new Ihr360RuntimeException("数据异常，根据code查询不到系统薪资字段！");
            }
            Map<String, String> systemSalaryFieldMap = systemSalaryFields.stream()
                    .collect(Collectors.toMap(SystemSalaryField::getCode, SystemSalaryField::getName));
            salaryProfileFields.forEach(salaryProfileField -> {
                salaryProfileField.setName(systemSalaryFieldMap.get(salaryProfileField.getCode()));
            });
        }
    }

    private void setSalaryfieldName(List<StaffPayrollInfoRequest> requestPayrollInfos, Map<String, String> systemStringStringMap) {
        for (StaffPayrollInfoRequest requestPayrollInfo : requestPayrollInfos) {
            List<SalaryProfileTypeItem> typeItems = requestPayrollInfo.getTypeItems();
            if (CollectionUtils.isEmpty(typeItems)) {
                continue;
            }
            for (SalaryProfileTypeItem typeItem : typeItems) {
                List<SalaryProfileFieldRequest> profileFields = typeItem.getProfileFields();
                if (CollectionUtils.isEmpty(profileFields)) {
                    continue;
                }
                for (SalaryProfileFieldRequest profileField : profileFields) {
                    String name = systemStringStringMap.get(profileField.getCode());
                    profileField.setName(name);
                }
            }
        }
    }

    private List<StaffSalaryProfileMapperEntity> getStaffSalaryMappers(List<Object[]> datas) {
        if (CollectionUtils.isEmpty(datas)) {
            return Lists.newArrayList();
        }
        List<StaffSalaryProfileMapperEntity> staffSalaryMapper = Lists.newArrayListWithCapacity(datas.size());
        for (Object[] mapperObj : datas) {
            StaffSalaryProfileMapperEntity mapper = new StaffSalaryProfileMapperEntity();
            String staffId = (String) mapperObj[0];
            String staffName = (String) mapperObj[1];
            mapper.setStaffId(staffId);
            mapper.setStaffName(staffName);
            if (mapperObj[2] != null) {
                String departmentName = (String) mapperObj[2];
                mapper.setDepartmentName(departmentName);
            }


            if (mapperObj[3] != null) {
                String staffSalaryProfileId = (String) mapperObj[3];
                mapper.setStaffSalaryProfileId(staffSalaryProfileId);
            }
            if (mapperObj[4] != null) {
                String mobileNo = (String) mapperObj[4];
                mapper.setMobileNo(mobileNo);
            }
            if (mapperObj[5] != null) {
                StaffStatus staffStatus = StaffStatus.valueOf((String) mapperObj[5]);
                mapper.setStaffStatus(staffStatus);
            }

            staffSalaryMapper.add(mapper);
        }
        return staffSalaryMapper;
    }


}
